package framework.aop.aspect;

import java.lang.reflect.Method;

/**
 * father class of aspect advise class
 */
public abstract class InchAbstractAspectAdvice {
    //aspect method that cut in eg: demo.aspect.logAspect.before
    private Method aspectMethod;
    //aspect class eg: demo.aspect.logAspect
    private Object aspectTarget;

    /**
     * Constructor
     * @param aspectMethod aspect method that cut in eg: demo.aspect.logAspect.before()
     * @param aspectTarget aspect class eg: demo.aspect.logAspect
     */
    public InchAbstractAspectAdvice(Method aspectMethod,Object aspectTarget) {
        this.aspectMethod = aspectMethod;
        this.aspectTarget = aspectTarget;
    }

    /**
     * invoke method for different aspect class
     * @param joinPoint aspect class eg: demo.aspect.logAspect
     * @param returnValue the value of the point cut
     * @param ex the exception the point cut throws
     * @return the result of the aspect method eg: demo.aspect.logAspect.before()
     * @throws Throwable
     */
    protected Object invokeAdviceMethod(InchJoinPoint joinPoint, Object returnValue, Throwable ex) throws Throwable{
        //get the types of the parameters of the aspectMethod
        //eg: demo.aspect.logAspect.afterReturning(InchJoinPoint joinPoint,Object result)  have 2 parameters
        Class<?>[] paramTypes = this.aspectMethod.getParameterTypes();
        // if have no parameters just run it
        if(paramTypes == null || paramTypes.length == 0) {
            return this.aspectMethod.invoke(aspectTarget);
        }else {
            //have parameters then initial parameters
            Object[] args = new Object[paramTypes.length];
            for(int i = 0; i < paramTypes.length; i++) {
                if(paramTypes[i] == InchJoinPoint.class) {
                    args[i] = joinPoint;
                }else if(paramTypes[i] == Throwable.class) {
                    args[i] = ex;
                //this parameter is the result of the point cut
                }else if(paramTypes[i] == Object.class) {
                    args[i] = returnValue;
                }
            }
            //run aspectMethod with parameter
            return this.aspectMethod.invoke(aspectTarget,args);
        }
    }
}
